﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace System.Collections.Generic
{
    public class DictionaryList<Tkey, Tvalue> : ILookup<Tkey, Tvalue>
    {
        private Dictionary<Tkey, IList<Tvalue>> dict;
        private readonly IList<Tvalue> NullList = new ReadOnly<Tvalue>();

        #region Constructors
        public DictionaryList()
        {
            dict = new Dictionary<Tkey, IList<Tvalue>>();
        }

        public DictionaryList(int capacity)
        {
            dict = new Dictionary<Tkey, IList<Tvalue>>(capacity);
        }

        public DictionaryList(IEqualityComparer<Tkey> comparer)
        {
            dict = new Dictionary<Tkey, IList<Tvalue>>((IEqualityComparer<Tkey>)comparer);
        }

        public DictionaryList(DictionaryList<Tkey, Tvalue> source)
        {
            if (source != null)
                dict = new Dictionary<Tkey, IList<Tvalue>>(source.dict);
            else
                dict = new Dictionary<Tkey, IList<Tvalue>>();
        }

        public DictionaryList(int capacity, IEqualityComparer<Tkey> comparer)
        {
            dict = new Dictionary<Tkey, IList<Tvalue>>(capacity, (IEqualityComparer<Tkey>)comparer);
        }

        public DictionaryList(DictionaryList<Tkey, Tvalue> source, IEqualityComparer<Tkey> comparer)
        {
            if (source != null)
                if (comparer != null)
                    dict = new Dictionary<Tkey, IList<Tvalue>>(source.dict, comparer);
                else
                    dict = new Dictionary<Tkey, IList<Tvalue>>(source.dict);
            else
                dict = new Dictionary<Tkey, IList<Tvalue>>();
        }
        #endregion

        public void Add(Tkey key, Tvalue value)
        {
            var lst = dict.GetOrAddDefault(key, () => new List<Tvalue>());
            lst.Add(value);
        }

        public bool Contains(Tkey key)
        {
            return dict.ContainsKey(key);
        }

        public ICollection<Tkey> Keys
        {
            get { return dict.Keys; }
        }

        public bool Remove(Tkey key)
        {
            return dict.Remove(key);
        }

        public int Count
        {
            get { return dict.Count; }
        }

        public IList<Tvalue> this[Tkey key]
        {
            get
            {
                return dict.GetOrDefault(key, NullList);                  
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return dict.GetEnumerator();
        }

        public IEnumerator<IGrouping<Tkey, Tvalue>> GetEnumerator()
        {
            return
            dict
                .SelectMany(s => s.Value
                    .Select(s1 => new KeyValuePair<Tkey, Tvalue>(s.Key, s1)))
                .GroupBy(s => s.Key, s => s.Value)
                .GetEnumerator();
        }

        public void Clear()
        {
            dict.Clear();
        }


        IEnumerable<Tvalue> ILookup<Tkey, Tvalue>.this[Tkey key]
        {
            get { return (IList<Tvalue>)this[key]; }
        }

        public bool TryGetList(Tkey key, out IList<Tvalue> value)
        {
            return dict.TryGetValue(key, out value);
        }

        public IEnumerable<Tvalue> GetOrAdd(Tkey key, Func<IEnumerable<Tvalue>> value)
        {
            return dict.GetOrAddDefault(key, () => value().ToList());
        }
    }
}
